﻿
using Common;
using UnityEngine;

namespace GameCamera
{
    
    public class FollowCameraController : ICameraController
    {
        private FollowStrategy m_FollowStrategy = FollowStrategy.SpringArmFollowStrategy;
        private Transform m_CameraTransform;
        private Transform m_LookAtTarget;
        private Transform m_FollowTarget;
        
        public void LookAt(Transform target)
        {
            m_LookAtTarget = target;
            m_FollowStrategy.Set(m_CameraTransform, m_FollowTarget, m_LookAtTarget);
        }
        
        public void Follow(Transform target)
        {
            m_FollowTarget = target;
            m_FollowStrategy.Set(m_CameraTransform, m_FollowTarget, m_LookAtTarget);
        }
        
        public void OnEnter(Camera camera)
        {
            m_CameraTransform = camera.transform;
            m_FollowStrategy.Set(m_CameraTransform, m_FollowTarget, m_LookAtTarget);
        }
        
        public void OnExit()
        {
            m_CameraTransform = null;
            m_LookAtTarget = null;
            m_FollowTarget = null;
        }
        
        public void FixedUpdate()
        {
            m_FollowStrategy?.DoFollow();
        }

        #region strategy
        

        public void SwitchFollowStrategy(FollowStrategy strategy)
        {
            if (strategy == null)
            {
                return;
            }
            m_FollowStrategy = strategy;
            m_FollowStrategy.Set(m_CameraTransform, m_FollowTarget, m_LookAtTarget);
        }
        

        #endregion
    }
    
    public abstract class FollowStrategy
    {
        protected Transform m_CameraTransform;
        protected Transform m_LookAtTarget;
        protected Transform m_FollowTarget;

        public void Set(Transform cameraTransform, Transform followTarget, Transform lookAtTarget)
        {
            m_CameraTransform = cameraTransform;
            m_LookAtTarget = lookAtTarget;
            m_FollowTarget = followTarget;
        }
        
        public abstract void DoFollow();
        
        public static readonly DefaultFollowStrategy DefaultFollowStrategy = new DefaultFollowStrategy();
        public static readonly SpringArmFollowStrategy SpringArmFollowStrategy = new SpringArmFollowStrategy();
    }
    
    public class DefaultFollowStrategy : FollowStrategy
    {
        private readonly Vector3 Offset = new Vector3(0, 3, -10);

        public override void DoFollow()
        {
            m_CameraTransform.position = CalculatePosition();
            m_CameraTransform.rotation = CalculateRotation();
        }

        private Vector3 CalculatePosition()
        {
            return m_FollowTarget.position + Offset;
        }

        private Quaternion CalculateRotation()
        {
            return Quaternion.Euler(45, 0, 0);
        }
    }

    public class SpringArmFollowStrategy : FollowStrategy
    {
        private readonly float RotateSpeed = 10f;
        
        private readonly float K = 5f; // 弹簧系数
        private readonly Vector3 Offset = new Vector3(0, 3, -10);
        private readonly int LayerMask = 0; // 避障层级
        
        public override void DoFollow()
        {
            if (m_FollowTarget != null)
            {
                m_CameraTransform.position = CalculatePosition();
            }

            if (m_LookAtTarget != null)
            {
                m_CameraTransform.rotation = CalculateRotation();
            }
        }
        
        internal Vector3 CalculatePosition()
        {
            var cameraPosition = m_CameraTransform.position;
            var targetPosition = m_FollowTarget.position;
            var cameraExpectedPosition = m_FollowTarget.TransformPoint(Offset);
            
            var direction = cameraExpectedPosition - cameraPosition;

            // 避障 
            var destPosition = Vector3.Lerp(cameraPosition, cameraPosition + K * direction, Time.deltaTime);
            var hit = Physics.Linecast(targetPosition, destPosition, out var hitInfo, LayerMask, QueryTriggerInteraction.Ignore);
            if (hit)
            {
                destPosition = hitInfo.point;
            }
            
            return destPosition;
        }

        private Quaternion CalculateRotation()
        {
            var lookQuaternion = Quaternion.LookRotation(m_LookAtTarget.position - m_CameraTransform.position);
            return Quaternion.Slerp(m_CameraTransform.rotation, lookQuaternion, Time.deltaTime * RotateSpeed);
        }
    }
    
    public class FreeLookStrategy : FollowStrategy
    {
        private readonly float RotateSpeed = 10f;

        public override void DoFollow()
        {
            if (m_FollowTarget != null)
            {
                m_CameraTransform.position = SpringArmFollowStrategy.CalculatePosition();
            }

            var rotationInput = LocalInput.GetLookInput();
            
            m_CameraTransform.Rotate( RotateSpeed * Time.deltaTime * rotationInput );
        }
        
    }
}